// Copyright (c) 2020 Presto Labs Pte. Ltd.
// Author: daniel

#pragma once

#include <algorithm>
#include <memory>
#include <utility>

#include "coin2/exchange/base/order/rate_tracker.h"

namespace coin2::exchange::order {
class OrderConnectionConfig;
}  // namespace coin2::exchange::order

namespace coin2::exchange::upbit::api_util {

class UpbitRateTracker {
  using RateTracker = coin2::exchange::base::order::RateTracker;

 public:
  UpbitRateTracker(
      int64_t sec_window_size,
      int32_t sec_rate_limit,
      int64_t min_window_size,
      int32_t min_rate_limit)
      : sec_rate_tracker_{std::make_unique<RateTracker>(sec_window_size)},
        min_rate_tracker_{std::make_unique<RateTracker>(min_window_size)} {
    sec_rate_tracker_->SetRateLimitInfo(sec_rate_limit, sec_rate_limit);
    min_rate_tracker_->SetRateLimitInfo(min_rate_limit, min_rate_limit);
  }

  bool HasEnoughRateLimit(int64_t timestamp) {
    return sec_rate_tracker_->HasEnoughRateLimitNoSafe(timestamp) &&
           min_rate_tracker_->HasEnoughRateLimitNoSafe(timestamp);
  }

  void Push(int32_t num_reqs, int64_t timestamp) {
    sec_rate_tracker_->Push(num_reqs, timestamp);
    min_rate_tracker_->Push(num_reqs, timestamp);
  }

  void UpdateRateLimitInfo(int32_t rate_limit_remain_sec, int32_t rate_limit_remain_min) {
    // TODO(daniel) - check remain of exchange side
  }

  double GetMaxRateUsedRatio(int64_t timestamp) {
    double min_ratio = 0.;
    if (min_rate_tracker_) {
      auto max_reqs = min_rate_tracker_->MaxReq();
      auto remaining = min_rate_tracker_->GetRemaining(timestamp);
      min_ratio = 1.0 * (max_reqs - remaining) / max_reqs;
    }
    double sec_ratio = 0.;
    if (sec_rate_tracker_) {
      auto max_reqs = sec_rate_tracker_->MaxReq();
      auto remaining = sec_rate_tracker_->GetRemaining(timestamp);
      sec_ratio = 1.0 * (max_reqs - remaining) / max_reqs;
    }
    return std::max(sec_ratio, min_ratio);
  }

 private:
  std::unique_ptr<RateTracker> sec_rate_tracker_;
  std::unique_ptr<RateTracker> min_rate_tracker_;
};

class RateTracker : public coin2::exchange::base::order::IRateTracker {
  using IRateTracker = coin2::exchange::base::order::IRateTracker;
  using NativeClientConfig = coin2::exchange::order::OrderConnectionConfig;
  using ActionType = coin2::exchange::base::order::IRateTracker::ActionType;
  using HttpContextPtr = coin2::exchange::base::order::IRateTracker::HttpContextPtr;
  using RateLimitReportWriter = coin2::exchange::base::order::RateLimitReportWriter;

 public:
  explicit RateTracker(const MarketExchangeApi& mea, const NativeClientConfig& config);

  void SetRateLimitInfo(int32_t rate_limit, int32_t rate_limit_remaining) override;
  void Push(int32_t num_reqs, int64_t timestamp, ActionType action_type) override;
  bool HasEnoughRateLimit(int64_t timestamp, ActionType action_type) override;
  void ParseRateLimitInfo(const HttpContextPtr& http_context, ActionType action_type) override;
  void SetRateLimitInfoString(const google::protobuf::Message* message) override;
  void WriteRateLimitReportMessage(OrderLoggerCB callback) override;
  double GetMaxRateUsedRatio(int64_t timestamp, ActionType action_type) override;

 private:
  std::unique_ptr<UpbitRateTracker> default_rate_tracker_;
  std::unique_ptr<UpbitRateTracker> order_rate_tracker_;
  std::unique_ptr<RateLimitReportWriter> rate_limit_report_writer_;
};

}  // namespace coin2::exchange::upbit::api_util
